package com.hontech.bv.conn

import com.hontech.bv.codec.decodeUint8
import com.hontech.bv.codec.xorSum
import com.hontech.bv.conn.serial.ByteView
import com.hontech.bv.conn.serial.Uint8
import com.hontech.bv.model.PickStatusEvent
import com.hontech.bv.model.arm.ArmStatus
import com.hontech.bv.model.heat.HeatStatus
import com.hontech.bv.model.ice.IceStatus
import com.hontech.bv.model.pearl.PearlStatus
import com.hontech.bv.model.pick.PickStatus
import com.hontech.bv.model.pipe_line.PipeLineStatus
import com.hontech.bv.model.powder.PowderStatus
import com.hontech.bv.task.eventBus
import com.hontech.bv.utils.Sync
import com.hontech.bv.utils.log
import kotlinx.coroutines.CompletableDeferred

class SerialPortReader(private val port: SerialPort) : Thread("serial-port-reader") {

    private val exitPromise = CompletableDeferred<Unit>()
    private val sync = Sync()
    private val router = Router()

    init {
        start()
    }

    suspend fun close() {
        port.notifyClose()
        exitPromise.await()
        port.close()
    }

    fun begin(frame: WriteFrame): CompletableDeferred<ReadFrame> {
        sync.reset()
        return router.set(frame)
    }

    fun waitAck(): Boolean {
        return sync.await(500)
    }

    override fun run() {

        while (true) {
            try {
                runOnce()
            } catch (e: IllegalStateException) {
                e.printStackTrace()
            } catch (e: Exception) {
                e.printStackTrace()
                break
            }
        }
        log("serial port reader exit")
        exitPromise.complete(Unit)
    }

    private fun runOnce() {
        val size = port.sync()
        if (size < 8) {
            throw IllegalStateException("size < 8 $size")
        }

        val buf = ByteArray(size - 3)
        port.readBytes(buf)

        val s = buf.xorSum(3, size - 8)

        val sum = decodeUint8(buf, buf.size - 2)
        val end = decodeUint8(buf, buf.size - 1)
        if (s != sum) {
            throw IllegalStateException("sum fail")
        }
        if (end != Proto.END) {
            throw IllegalStateException("end fail")
        }

        val frame = ReadFrame(buf)
        dispatch(frame)
    }

    private fun dispatch(frame: ReadFrame) {
      //  log("recv:${frame}")
        when (frame.type) {
            Proto.TYPE_REQUEST -> onRequest(frame)
            Proto.TYPE_RESPONSE -> onResponse(frame)
            Proto.TYPE_NOTIFY -> onNotify(frame)
        }
    }

    private fun onRequest(frame: ReadFrame) {

    }

    private fun onResponse(frame: ReadFrame) {
        when (frame.req) {
            Proto.REQ_ACK -> sync.signal()
            else -> {
                router.notify(frame)
                port.writeAck(frame.src, frame.seq)
            }
        }
    }

    private fun onNotify(frame: ReadFrame) {
        when (frame.req) {
            Proto.REQ_LOG -> recvLog(frame)
            Proto.REQ_STATUS -> dispatchStatus(frame)
            Proto.REQ_PICK_NOTIFY -> pickNotify(frame)
        }
    }

    private fun recvLog(frame: ReadFrame) {
        val bw = ByteView()
        frame.parse(arrayOf(bw))
        log("log: $bw")
    }

    private fun pickNotify(frame: ReadFrame) {
        val prog = Uint8()
        val err = Uint8()
        frame.parse(arrayOf(prog, err))
        val msg = "${Proto.progName(prog.value)}:${ErrCode.msg(err.value)}"
        eventBus.post(PickStatusEvent(prog.value, err.value, msg))
    }

    private fun dispatchStatus(frame: ReadFrame) {
        when (frame.src) {
            Proto.ID_PICK -> PickStatus.update(frame)
            Proto.ID_ARM -> ArmStatus.update(frame)
            Proto.ID_POWDER -> PowderStatus.update(frame)
            Proto.ID_PEARL -> PearlStatus.update(frame)
            Proto.ID_HEAT -> HeatStatus.update(frame)
            Proto.ID_MASTER -> PipeLineStatus.update(frame)
            Proto.ID_ICE -> IceStatus.update(frame)
        }
    }

}











